﻿#pragma once
#include "zyBase.h"

namespace BlackMoonUI {
    zyBase::zyBase() : m_obj(zyObject::Create()) {
        m_obj->setWndProc(zyBase_WndProc);
        m_obj->setChildWndProc(zyBase_ChildWndProc);
        m_obj->setDestroy(zyBase_Destroy);
    }

    zyBase::~zyBase() {
        m_obj->release();
    }

    zyObject* zyBase::getObject(BOOL bAddRef) const {
        if (bAddRef) m_obj->addRef();
        return m_obj;
    }

    zyObject* zyBase::取对象(BOOL 添加引用) const {
        return getObject(添加引用);
    }

    BOOL zyBase::setObject(zyObject* newObj, BOOL bAddRef) {
        if (newObj == NULL || !m_obj->checkSelf()) return FALSE;
        if (newObj == m_obj) return TRUE;
        m_obj->release();
        m_obj = newObj;
        if (bAddRef) m_obj->addRef();
        return TRUE;
    }

    BOOL zyBase::置对象(zyObject* 新对象, BOOL 添加引用) {
        return setObject(新对象, 添加引用);
    }

    BOOL zyBase::setHwnd(HWND hwnd) {
        if (hwnd && IsWindow(hwnd)) {
            auto newObj = zyEnv::getInstance().getWndObject(hwnd);
            return setObject(newObj);
        }
        return FALSE;
    }

    BOOL zyBase::置窗口句柄(HWND 窗口句柄) {
        return setHwnd(窗口句柄);
    }

    HWND zyBase::getHwnd() const {
        return (HWND)m_obj->getPtrData(L"hWnd");
    }

    HWND zyBase::取窗口句柄() const {
        return getHwnd();
    }

    int zyBase::getBorder() const {
        return (int)(LONG_PTR)m_obj->getPtrData(L"Border");
    }

    void zyBase::setBorder(int border) {
        m_obj->setPtrData(L"Border", (void*)(LONG_PTR)border);
    }

    BOOL zyBase::hasBorder() const {
        return m_obj->hasPtrData(L"Border");
    }

    std::wstring zyBase::getType() const {
        return m_obj->getStringData(L"Type"); // 返回空字符串作为默认值
    }

    void zyBase::setType(const std::wstring& type) {
        m_obj->setStringData(L"Type", type);
    }


    std::wstring zyBase::getName() const {
        return m_obj->getStringData(L"name"); // 返回空字符串作为默认值
    }

    void zyBase::setName(const std::wstring& name) {
        m_obj->setStringData(L"name", name);
    }

    BOOL zyBase::HasMember(const std::wstring& name) {
        return m_obj->hasPtrData(name);
    }

    LONG_PTR zyBase::GetMember(const std::wstring& name) const {
        return (LONG_PTR)m_obj->getPtrData(name);
    }

    void zyBase::SetMember(const std::wstring& name, LONG_PTR border) {
        m_obj->setPtrData(name, (void*)border);
    }

    void zyBase::SetRegEvent(const std::wstring& name, TRegEvent* pRegEvent) {
        m_obj->setRegEvent(name, pRegEvent);
    }

    TRegEvent* zyBase::GetRegEvent(const std::wstring& name) const {
        return m_obj->getRegEvent(name);
    }

    int zyBase::GetFunKeyState() {
        int ret = 0;
        if (GetAsyncKeyState(VK_CONTROL) & 0x8000) ret |= 0x1;
        if (GetAsyncKeyState(VK_SHIFT) & 0x8000) ret |= 0x2;
        if (GetAsyncKeyState(VK_MENU) & 0x8000) ret |= 0x4;
        return ret;
    }

    BOOL zyBase::SysHotKeyHandler(HWND hWnd, const std::wstring& className, wchar_t wchr) {
        if (IsWindow(hWnd)) {
            wchar_t hotKeybuf[2] = {'&', wchr};
            wchar_t* lpNameBuff = new wchar_t[MAX_PATH];

            HWND hWndNext = FindWindowExW(hWnd, NULL, NULL, NULL);
            while (hWndNext) {
                if (GetClassNameW(hWndNext, lpNameBuff, MAX_PATH) > 0 && className == lpNameBuff) {
                    int len = GetWindowTextLengthW(hWndNext) + 1;
                    if (len > 0) {
                        if (GetWindowTextW(hWndNext, lpNameBuff, MAX_PATH) > 0) {
                            if (wcsstr(lpNameBuff, hotKeybuf)) {
                                HWND hwndParent = GetParent(hWndNext);
                                if (hwndParent != nullptr) {
                                    auto id = GetWindowLongPtrW(hWndNext, GWL_ID);
                                    SendMessage(hwndParent, WM_COMMAND, MAKEWPARAM(id, BN_CLICKED), (LPARAM)hWndNext);
                                    return TRUE;
                                }
                            }
                        }
                    }
                }
                hWndNext = FindWindowExW(hWnd, hWndNext, nullptr, nullptr);
            }
            delete[] lpNameBuff;
        }
        return FALSE;
    }

    void zyBase::zyBase_Destroy(zyObject* obj) {
        zyBase_OnNCDestroy(obj);
        obj->removeStringData(L"Name");
    }

    void zyBase::zyBase_OnNCDestroy(zyObject* obj) {
        HWND hWnd = (HWND)obj->getPtrData(L"hWnd");
        if (hWnd) {
            LONG_PTR oldWndProc = (LONG_PTR)obj->getPtrData(L"oldWndProc");
            if (oldWndProc) {
                SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc);
                obj->removePtrData(L"oldWndProc");
            }
            zyEnv::getInstance().unregisterWnd(hWnd);
            obj->removePtrData(L"hWnd");
        }
        zyObject* layout = (zyObject*)obj->getPtrData(L"zyLayout");
        if (layout) {
            layout->release();
            obj->removePtrData(L"zyLayout");
        }
        obj->removePtrData(L"isMoveIn");
    }

    LRESULT CALLBACK zyBase::zyBase_WndProc(zyObject* obj, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL* bHandled) {
        LRESULT ret = 0;
        *bHandled = FALSE;
        auto pEvent = obj->getRegEvent(L"OnWndMsg");
        if (pEvent) {
            ret = pEvent->handler<LRESULT>(hWnd, uMsg, wParam, lParam, bHandled);
            if (*bHandled)
                return ret;
        }
        
        switch (uMsg) {
        case WM_NCDESTROY:
            zyBase_OnNCDestroy(obj);
            break;
        case WM_PAINT: {
            pEvent = obj->getRegEvent(L"OnPaint");
            auto hRgn = (HRGN)obj->getPtrData(L"WindowhRgn");
            if (hRgn)
                SetWindowRgn(hWnd, hRgn, TRUE);
            if (pEvent) {
                PAINTSTRUCT ps = { 0 };
                BeginPaint(hWnd, &ps);
                ret = pEvent->handler<LRESULT>(hWnd, ps.hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
                *bHandled = TRUE;
                EndPaint(hWnd, &ps);
            }
            if (*bHandled)
                return ret;
        } 
        break;
        // 鼠标左键被按下
        case WM_LBUTTONDOWN:
        {
            pEvent = obj->getRegEvent(L"OnLButtonDown");
            if (pEvent) {
                ret = pEvent->handler<LRESULT>(hWnd, (int)LOWORD(lParam), (int)HIWORD(lParam), GetFunKeyState());
                if (ret) {
                    *bHandled = TRUE;
                    return ret;
                }
            }
        }
        break;
        // 鼠标左键被释放
        case WM_LBUTTONUP:
        {
            pEvent = obj->getRegEvent(L"OnLButtonUp");
            if (pEvent) {
                ret = pEvent->handler<LRESULT>(hWnd, (int)LOWORD(lParam), (int)HIWORD(lParam), GetFunKeyState());
                if (ret) {
                    *bHandled = TRUE;
                    return ret;
                }
            }
        }
        break;
        // 鼠标左键双击
        case WM_LBUTTONDBLCLK:
        {
            pEvent = obj->getRegEvent(L"OnLButtonDblClk");
            if (pEvent) {
                ret = pEvent->handler<LRESULT>(hWnd, (int)LOWORD(lParam), (int)HIWORD(lParam), GetFunKeyState());
                if (ret) {
                    *bHandled = TRUE;
                    return ret;
                }
            }
        }
        break;
        // 鼠标右键被按下
        case WM_RBUTTONDOWN:
        {
            pEvent = obj->getRegEvent(L"OnRButtonDown");
            if (pEvent) {
                ret = pEvent->handler<LRESULT>(hWnd, (int)LOWORD(lParam), (int)HIWORD(lParam), GetFunKeyState());
                if (ret) {
                    *bHandled = TRUE;
                    return ret;
                }
            }
        }
        break;
        // 鼠标右键被释放
        case WM_RBUTTONUP:
        {
            pEvent = obj->getRegEvent(L"OnRButtonUp");
            if (pEvent) {
                ret = pEvent->handler<LRESULT>(hWnd, (int)LOWORD(lParam), (int)HIWORD(lParam), GetFunKeyState());
                if (ret) {
                    *bHandled = TRUE;
                    return ret;
                }
            }
        }
        break;
        // 鼠标移动
        case WM_MOUSEMOVE: {
            if (obj->hasPtrData(L"EnlableHover")) {
                auto isMoveIn = (size_t)obj->getPtrData(L"isMoveIn");
                if (isMoveIn == 0) {
                    obj->setPtrData(L"isMoveIn", (void*)1);

                    pEvent = obj->getRegEvent(L"OnMouseIn");
                    if (pEvent)
                        ret = pEvent->handler<LRESULT>(hWnd, LOWORD(lParam), HIWORD(lParam), GetFunKeyState());
                    TRACKMOUSEEVENT tme = {
                        sizeof(tme),
                        TME_HOVER | TME_LEAVE,
                        hWnd,
                        HOVER_DEFAULT };
                    TrackMouseEvent(&tme);
                }
            }
            pEvent = obj->getRegEvent(L"OnMouseMove");
            if (pEvent) {
                ret = pEvent->handler<LRESULT>(hWnd, LOWORD(lParam), HIWORD(lParam), GetFunKeyState());
                if (ret) {
                    *bHandled = TRUE;
                    return ret;
                }
            }
        } 
        break;
        // 鼠标离开
        case WM_MOUSELEAVE: case 674:
        {
            obj->setPtrData(L"isMoveIn", (void*)0);
            pEvent = obj->getRegEvent(L"OnMouseLeave");
            if (pEvent)
                pEvent->handler<LRESULT>(hWnd);
        }
        break;
        // 鼠标悬停
        case WM_MOUSEHOVER:
        {
            pEvent = obj->getRegEvent(L"OnMouseHover");
            if (pEvent)
                pEvent->handler<LRESULT>(hWnd);
        }
        break;
        // 获取焦点
        case WM_SETFOCUS:
        {
            pEvent = obj->getRegEvent(L"OnSetFocus");
            if (pEvent)
                pEvent->handler<LRESULT>(hWnd);
        }
        break;
        // 失去焦点
        case WM_KILLFOCUS:
        {
            pEvent = obj->getRegEvent(L"OnKillFocus");
            if (pEvent)
                pEvent->handler<LRESULT>(hWnd);
        }
        break;
        // 键盘按下
        case WM_KEYDOWN:
        case WM_SYSKEYDOWN:
        {
            pEvent = obj->getRegEvent(L"OnKeyDown");
            if (pEvent) {
                ret = pEvent->handler<LRESULT>(hWnd, wParam, GetFunKeyState());
                if (ret) {
                    *bHandled = TRUE;
                    return ret;
                }
            }
        }
        break;
        // 键盘释放
        case WM_KEYUP:
        case WM_SYSKEYUP:
        {
            pEvent = obj->getRegEvent(L"OnKeyUp");
            if (pEvent) {
                ret = pEvent->handler<LRESULT>(hWnd, wParam, GetFunKeyState());
                if (ret) {
                    *bHandled = TRUE;
                    return ret;
                }
            }
        }
        break;
        // 滚轮滚动
        case WM_MOUSEWHEEL:
        {
            pEvent = obj->getRegEvent(L"OnMouseWheel");
            if (pEvent)
                pEvent->handler<LRESULT>(hWnd, GET_WHEEL_DELTA_WPARAM(wParam), GetFunKeyState());
        }
        break;
        // 字符输入
        case WM_CHAR:
        {
            pEvent = obj->getRegEvent(L"OnChar");
            if (pEvent) {
                ret = pEvent->handler<LRESULT>(hWnd, wParam);
                if (ret) {
                    *bHandled = TRUE;
                    return ret;
                }
            }
        }
        break;
        //滚动条滚动
        case WM_HSCROLL:
        case WM_VSCROLL: {
            auto pObj = zyEnv::getInstance().getWndObject((HWND)lParam);
            if (pObj)
                return pObj->callChildWndProc(hWnd, uMsg, wParam, lParam, bHandled);
        } break;
        // 命令消息
        case WM_COMMAND: 
        {
            auto Ptr = zyEnv::getInstance().getWndObject((HWND)lParam);
            if (Ptr) {
                return Ptr->callChildWndProc(hWnd, uMsg, wParam, lParam, bHandled);
            }
        } 
        break;
        // 通知消息
        case WM_NOTIFY: {
            zyObject* pObj = NULL;
            auto nh = *reinterpret_cast<NMHDR*>(lParam);
            if (nh.code == TTN_NEEDTEXTW)
                pObj = zyEnv::getInstance().getWndObject(hWnd);
            else
                pObj = zyEnv::getInstance().getWndObject(nh.hwndFrom);
            if (pObj)
                return pObj->callChildWndProc(hWnd, uMsg, wParam, lParam, bHandled);
        } break;
        // 绘制项目
        case WM_DRAWITEM: {
            DRAWITEMSTRUCT dis = *(DRAWITEMSTRUCT*)lParam;
            auto pObj = zyEnv::getInstance().getWndObject(dis.hwndItem);
            if (pObj)
                return pObj->callChildWndProc(hWnd, uMsg, wParam, lParam, bHandled);
        } break;
        // 拖放文件
        case WM_DROPFILES: {
            pEvent = obj->getRegEvent(L"OnDropFiles");
            if (pEvent) {
                UINT countDrop = DragQueryFileW((HDROP)wParam, -1, NULL, 0);
                WCHAR szFile[MAX_PATH] = {0};
                std::wstring files;
                int len;
                for (UINT i = 0; i < countDrop; i++) {
                    len = DragQueryFileW((HDROP)wParam, i, szFile, MAX_PATH);
                    if (len > 0) {
                        files += szFile;
                        if (i + 1 < countDrop)
                            files += L"\r\n";
                    }
                }
                pEvent->handler<LRESULT>(hWnd, files.c_str());
                DragFinish((HDROP)wParam);
                *bHandled = TRUE;
                return 0;
            }
        }
        // 定时器消息
        case WM_TIMER:
        {
            pEvent = obj->getRegEvent(L"OnTimer");
            if (pEvent) {
                pEvent->handler<LRESULT>(hWnd, wParam);
                *bHandled = TRUE;
                return 0;
            }
        }
        break;
        // 列表消息
        case WM_MEASUREITEM:
        case WM_DELETEITEM:
        case WM_COMPAREITEM: {
            {
                auto hWndChild = GetDlgItem(hWnd, (int)wParam);
                if (hWndChild) {
                    auto pObj = zyEnv::getInstance().getWndObject(hWndChild);
                    if (pObj) {
                        ret = pObj->callChildWndProc(hWnd, uMsg, wParam, lParam, bHandled);
                        if (*bHandled)
                            return ret;
                    }
                }
            }
        }
        break;
        // 大小改变
        case WM_SIZE: {
            auto layout = (zyObject*)obj->getPtrData(L"zyLayout");
            if (layout) {
                layout->callWndProc(hWnd, zyLYM_SETSIZE, LOWORD(lParam), HIWORD(lParam), bHandled);
                layout->callWndProc(hWnd, zyLYM_LAYOUT, LOWORD(lParam), HIWORD(lParam), bHandled);
            }
            break;
        }
        // 系统字符键
        case WM_SYSCHAR: {
            auto hWndParent = GetAncestor(hWnd, GA_ROOT);
            if (SysHotKeyHandler(hWndParent, L"Button", (wchar_t)wParam)) {
                *bHandled = TRUE;
                return 0;
            }
            if (SysHotKeyHandler(hWndParent, L"zyButton", (wchar_t)wParam)) {
                *bHandled = TRUE;
                return 0;
            }
        } break;
        default: {
            // 静态控件 编辑控件 列表控件 设置背景或前景色
            if (uMsg >= WM_CTLCOLORMSGBOX && uMsg <= WM_CTLCOLORSTATIC) {
                zyObject* pObj = zyEnv::getInstance().getWndObject((HWND)lParam);
                if (pObj != nullptr) {
                    ret = pObj->callChildWndProc(hWnd, uMsg, wParam, lParam, bHandled);
                    if (*bHandled)
                        return ret;
                }
            } break;
        }
        }
        return 0;
    }

    LRESULT CALLBACK zyBase::zyBase_ChildWndProc(zyObject* obj, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL* bHandled) {
        //std::cout << "WM_COMMAND: " << (uMsg == WM_COMMAND) << std::endl;
        return 0;
    }

}